/*
 /	This file is part of Answerkeys Disabler
 /
 /	Answerkeys Disabler is free software: you can redistribute it and/or modify
 /	it under the terms of the GNU General Public License as published by
 /	the Free Software Foundation, either version 3 of the License, or
 /	(at your option) any later version.
 /
 /	Answerkeys Disabler is distributed in the hope that it will be useful,
 /	but WITHOUT ANY WARRANTY; without even the implied warranty of
 /	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 /	GNU General Public License for more details.
 /
 /	You should have received a copy of the GNU General Public License
 /	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 /
 /	Copyright 2008 Daan van Dijk (drvdijk@gmail.com)
 */

#include "StdAfx.h"
#include "TAPIDevice.h"

HANDLE CTAPIDevice::g_hIncomingCallEvent = NULL;
HANDLE CTAPIDevice::g_hCallConnectedEvent = NULL;
HANDLE CTAPIDevice::g_hCallEndedEvent = NULL;

CTAPIDevice::CTAPIDevice(void)
{
	m_hLineApp				= NULL;
	m_hLine					= NULL;
	m_dwLowAPIVersion		= 0;
	m_dwDevices				= 0;
	g_hIncomingCallEvent	= NULL;
	g_hCallConnectedEvent	= NULL;
	g_hCallEndedEvent		= NULL;
}

CTAPIDevice::~CTAPIDevice(void)
{
}

void FAR PASCAL CTAPIDevice::lineCallback(DWORD hDevice,
										  DWORD dwMsg,
										  DWORD dwCallbackInstance,
										  DWORD dwParam1,
										  DWORD dwParam2,
										  DWORD dwParam3)
{

	if (dwMsg == LINE_CALLSTATE) {
		if (dwParam1 == LINECALLSTATE_OFFERING && dwParam2 == LINEOFFERINGMODE_ACTIVE) {
			SetEvent(g_hIncomingCallEvent);
		}
		else if (dwParam1 == LINECALLSTATE_CONNECTED) {
			SetEvent(g_hCallConnectedEvent);
		}
		else if (dwParam1 == LINECALLSTATE_IDLE) {
			SetEvent(g_hCallEndedEvent);
		}
	}
}


BOOL CTAPIDevice::Initialize(IN	HINSTANCE		hInstance,
							 IN	TCHAR*			pszAppName	/* = NULL */)
{
	BOOL					bReturn		= FALSE;
	LINEINITIALIZEEXPARAMS	sLineParam	= {0};

	memset(&sLineParam,0,sizeof(LINEINITIALIZEEXPARAMS));

	sLineParam.dwTotalSize	= sizeof(LINEINITIALIZEEXPARAMS);
	sLineParam.dwOptions	= LINEINITIALIZEEXOPTION_USEHIDDENWINDOW; 

	m_dwLowAPIVersion		= TAPI_CURRENT_VERSION;

	long	lReturn	= lineInitializeEx(&m_hLineApp,
									   hInstance,
									   lineCallback,
									   TAPI_FRIENDLY_NAME,
									   &m_dwDevices,
									   &m_dwLowAPIVersion,
									   &sLineParam);

	if(0 == lReturn)
	{
		m_dwCellularId	= GetCellularLineId();
		g_hIncomingCallEvent = CreateEvent(NULL, FALSE, FALSE, INCOMING_CALL_EVENT_NAME);
		g_hCallConnectedEvent = CreateEvent(NULL, FALSE, FALSE, CALL_CONNECTED_EVENT_NAME);
		g_hCallEndedEvent = CreateEvent(NULL, FALSE, FALSE, CALL_ENDED_EVENT_NAME);
	}

	return (0 == lReturn);
}

DWORD CTAPIDevice::GetCellularLineId()
{
	DWORD				dwReturn		= 0;
	long				lResult			= 0;
	LINEEXTENSIONID		sLineExt		= {0};
	LPLINEDEVCAPS		lpLineDevCaps	= NULL;	
	DWORD				dwAPIVersion	= TAPI_CURRENT_VERSION;
	BOOL				bContinue		= TRUE;

	for(DWORD dwLine=0; dwLine<m_dwDevices && bContinue; ++dwLine)
	{
		lResult		= lineNegotiateAPIVersion(m_hLineApp,dwLine,m_dwLowAPIVersion,TAPI_CURRENT_VERSION,&dwAPIVersion,&sLineExt);

		if(0 == lResult)
		{
			lpLineDevCaps	= (LPLINEDEVCAPS)LocalAlloc(LPTR,sizeof(LINEDEVCAPS));
			lResult			= LINEERR_STRUCTURETOOSMALL;

			lpLineDevCaps->dwTotalSize	= sizeof(LINEDEVCAPS);
			lpLineDevCaps->dwNeededSize	= sizeof(LINEDEVCAPS);

			while(LINEERR_STRUCTURETOOSMALL == lResult)
			{
				lResult	= lineGetDevCaps(m_hLineApp,dwLine,TAPI_CURRENT_VERSION,0,lpLineDevCaps);

				if(LINEERR_STRUCTURETOOSMALL == lResult || lpLineDevCaps->dwTotalSize < lpLineDevCaps->dwNeededSize)
				{
					lpLineDevCaps	= (LPLINEDEVCAPS)LocalReAlloc(lpLineDevCaps,lpLineDevCaps->dwNeededSize,LMEM_MOVEABLE);
					lResult			= LINEERR_STRUCTURETOOSMALL;

					lpLineDevCaps->dwTotalSize	= lpLineDevCaps->dwNeededSize;
				}
			}

			if(0 == lResult)
			{
				TCHAR szName[512];

				memcpy((PVOID)szName,(PVOID)((BYTE*)lpLineDevCaps + lpLineDevCaps ->dwLineNameOffset), 
					    lpLineDevCaps->dwLineNameSize);

				szName[lpLineDevCaps->dwLineNameSize]	= 0;

				if(_tcscmp(szName,CELLTSP_LINENAME_STRING) == 0)
				{
					dwReturn	= dwLine;
					bContinue	= FALSE;
				}
			}

			LocalFree((HLOCAL)lpLineDevCaps);
		}
	}

	return dwReturn;
}

void CTAPIDevice::GetGeneralInfo(TCHAR* pszIMEI,TCHAR* pszManufacturer,TCHAR* pszModal,TCHAR* pszSubscriber,TCHAR* pszRevision)
{
	DWORD	dwMediaMode = LINEMEDIAMODE_INTERACTIVEVOICE;
	HLINE	hLine		= NULL;
	long	lReturn		= lineOpen(m_hLineApp,m_dwCellularId,&hLine,
								   TAPI_CURRENT_VERSION,0,(DWORD)this,
								   LINECALLPRIVILEGE_OWNER,dwMediaMode,0);

	if(0 != lReturn || !hLine)
	{
		return;
	}

	LINEGENERALINFO* pInfo = (LINEGENERALINFO*)LocalAlloc(LPTR,sizeof(LINEGENERALINFO));

	pInfo->dwTotalSize	= sizeof(LINEGENERALINFO);
	lReturn				= LINEERR_STRUCTURETOOSMALL;

	while(LINEERR_STRUCTURETOOSMALL == lReturn)
	{
		lReturn	= lineGetGeneralInfo(hLine,pInfo);
		if(LINEERR_STRUCTURETOOSMALL == lReturn || pInfo->dwTotalSize < pInfo->dwNeededSize)
		{
			pInfo	= (LINEGENERALINFO*)LocalReAlloc(pInfo,pInfo->dwNeededSize,LMEM_MOVEABLE);
			lReturn	= LINEERR_STRUCTURETOOSMALL;
			pInfo->dwTotalSize	= pInfo->dwNeededSize;
		}
	}

	if(0 == lReturn)
	{
		TCHAR	szInfo[MAX_PATH];

		if(pInfo->dwSerialNumberSize > 0 && pszIMEI)
		{
			GetLineGeneralInfo(pInfo,szInfo,pInfo->dwSerialNumberSize,pInfo->dwSerialNumberOffset);
			_tcscpy(pszIMEI,szInfo);
		}

		if(pInfo->dwManufacturerSize > 0 && pszManufacturer)
		{
			GetLineGeneralInfo(pInfo,szInfo,pInfo->dwManufacturerSize,pInfo->dwManufacturerOffset);
			_tcscpy(pszManufacturer,szInfo);
		}

		if(pInfo->dwModelSize > 0 && pszModal)
		{
			GetLineGeneralInfo(pInfo,szInfo,pInfo->dwModelSize,pInfo->dwModelOffset);
			_tcscpy(pszModal,szInfo);
		}

		if(pInfo->dwSubscriberNumberSize > 0 && pszSubscriber)
		{
			GetLineGeneralInfo(pInfo,szInfo,pInfo->dwSubscriberNumberSize,pInfo->dwSubscriberNumberOffset);
			_tcscpy(pszSubscriber,szInfo);
		}

		if(pInfo->dwRevisionSize > 0 && pszRevision)
		{
			GetLineGeneralInfo(pInfo,szInfo,pInfo->dwRevisionSize,pInfo->dwRevisionOffset);
			_tcscpy(pszRevision,szInfo);
		}
	}

	LocalFree((HLOCAL)pInfo);
	lineClose(hLine);
}

BOOL CTAPIDevice::GetLineGeneralInfo(LPLINEGENERALINFO pInfo,LPTSTR pszInfo,DWORD dwInfo,DWORD dwOffset)
{
	if(dwInfo <= 0)
		return FALSE;
	for(int i = 0; i <(signed)(dwInfo/2); ++i)
	{
		pszInfo[i]	= (char)(*((unsigned short *)(pInfo) + i + dwOffset/2));
		pszInfo[i+1]	= NULL;
	}
	return TRUE;
}

BOOL CTAPIDevice::OpenLine(DWORD dwId /*= -1*/,DWORD dwPrivileges /*= LINECALLPRIVILEGE_MONITOR*/)
{
	if(dwId == -1)
	{
		dwId	= m_dwCellularId; 
	}

	DWORD	dwMediaMode = LINEMEDIAMODE_INTERACTIVEVOICE;
	HLINE	hLine		= NULL;
	long	lReturn		= lineOpen(m_hLineApp,dwId,&m_hLine,
								   TAPI_CURRENT_VERSION,0,(DWORD)this,
								   dwPrivileges,dwMediaMode,0);
	return (0 == lReturn);
}

BOOL CTAPIDevice::CloseLine()
{
	if(m_hLine)
	{
		lineClose(m_hLine);
		m_hLine	= NULL;
	}
	return TRUE;
}

void CTAPIDevice::Shutdown()
{
	CloseLine();
	if(m_hLineApp)
	{
		lineShutdown(m_hLineApp);
		m_hLineApp	= NULL;
	}
	CloseHandle(g_hIncomingCallEvent);
	CloseHandle(g_hCallEndedEvent);
}
